﻿using System;
using System.Collections.Generic;
using System.Globalization;
using LoD.WPClientLibrary.Animations;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;


namespace LoD.WPClientLibrary {
    public class Sprite {
        private Vector2 _size;

        public Sprite(ContentManager content, SpriteBatch spriteBatch) {
            this.ContentManager = content;
            this.SpriteBatch = spriteBatch;
            this.Image = null;
            this.Position = Vector2.Zero;
            this.VelocityLinear = Vector2.Zero;
            this.Color = Color.White;
            this.Rotation = 0.0f;
            this.VelocityAngular = 0.0f;
            this.ScaleV = new Vector2(1.0f);
            this.Origin = Vector2.Zero;
            this.Alive = true;
            this.Visible = true;
            this.Animations = new List<Animation>();
            this.Size = Vector2.Zero;
            this.Columns = 1;
            this.Frame = 0;
            this.TotalFrames = 1;
            this.Zindex = 0.0f;
        }

        public bool Alive { get; set; }
        public List<Animation> Animations { get; set; }
        public Color Color { get; set; }
        public int Columns { get; set; }
        public int Frame { get; set; }
        public Texture2D Image { get; set; }
        public Vector2 Origin { get; set; }
        protected ContentManager ContentManager { get; set; }
        protected SpriteBatch SpriteBatch { get; set; }
        public Vector2 Position { get; set; }
        public float Rotation { get; set; }
        public Vector2 ScaleV { get; set; }
        public int TotalFrames { get; set; }
        public float VelocityAngular { get; set; }
        public Vector2 VelocityLinear { get; set; }
        public bool Visible { get; set; }
        public float Zindex { get; set; }


        public float X {
            get { return this.Position.X; }
            set { this.Position = new Vector2(value, this.Position.Y); }
        }

        public float Y {
            get { return this.Position.Y; }
            set { this.Position = new Vector2(this.Position.X, value); }
        }

        public Vector2 Size {
            get { return this._size; }
            set {
                this._size = value;
                this.Origin = new Vector2(this._size.X / 2, this._size.Y / 2);
            }
        }

        public float Scale {
            get { return this.ScaleV.X; }
            set { this.ScaleV = new Vector2(value); }
        }

        public virtual bool Load(string assetName) {
            try {
                this.Image = this.ContentManager.Load<Texture2D>(assetName);
            } catch (Exception) {
                return false;
            }

            this.Size = new Vector2(this.Image.Width, this.Image.Height);
            // ReSharper disable PossibleLossOfFraction
            this.Origin = new Vector2(this.Image.Width / 2, this.Image.Height / 2);
            // ReSharper restore PossibleLossOfFraction

            return true;
        }

        public virtual void Draw() {
            if (!this.Visible) return;

            if (this.Image == null) return;

            if (this.TotalFrames > 1) {
                var source = new Rectangle {
                    X = (this.Frame % this.Columns) * (int)this.Size.X,
                    Y = (this.Frame / this.Columns) * (int)this.Size.Y,
                    Width = (int)this.Size.X,
                    Height = (int)this.Size.Y
                };
                this.SpriteBatch.Draw(this.Image, this.Position, source, this.Color,
                                      this.Rotation, this.Origin, this.Scale, SpriteEffects.None, this.Zindex);
            } else {
                this.SpriteBatch.Draw(this.Image, this.Position, null, this.Color, this.Rotation,
                                      this.Origin, this.ScaleV, SpriteEffects.None, this.Zindex);
            }
        }

        public void Move() { this.Position += this.VelocityLinear; }

        public void Rotate() {
            if (Math.Abs(this.VelocityAngular - 0.0f) < float.Epsilon) return;
            this.Rotation += this.VelocityAngular;
            if (this.Rotation > Math.PI * 2) this.Rotation -= (float)Math.PI * 2;
            else if (this.Rotation < 0.0f) this.Rotation = (float)Math.PI * 2 - this.Rotation;
        }

        public Rectangle Boundary() {
            var halfw = (int)((this.Size.X / 2) * this.ScaleV.X);
            var halfh = (int)((this.Size.Y / 2) * this.ScaleV.Y);

            return new Rectangle(
                (int)this.Position.X - halfw,
                (int)this.Position.Y - halfh,
                halfw * 2,
                halfh * 2);
        }

        public override string ToString() {
            string s =
                string.Format(
                              "Texture {{W:{0} H:{1}}}\n" + "Position {{X:{2} Y:{3}}}\n" + "Lin Vel {4}\n"
                              + "Ang Vel {{{5}}}\n" + "Scaling {6}\n" + "Rotation {7}\n" + "Pivot {8}\n",
                              this.Image.Width.ToString(CultureInfo.InvariantCulture),
                              this.Image.Height.ToString(CultureInfo.InvariantCulture), this.Position.X.ToString("N2"),
                              this.Position.Y.ToString("N2"), this.VelocityLinear.ToString(),
                              this.VelocityAngular.ToString("N2"), this.ScaleV.ToString(),
                              this.Rotation.ToString(CultureInfo.InvariantCulture), this.Origin.ToString());
            Rectangle b = this.Boundary();
            s += string.Format("Boundary {{X:{0} Y:{1} W:{2} H:{3}}}\n", b.X.ToString(CultureInfo.InvariantCulture),
                               b.Y.ToString(CultureInfo.InvariantCulture),
                               b.Width.ToString(CultureInfo.InvariantCulture),
                               b.Height.ToString(CultureInfo.InvariantCulture));
            return s;
        }

        public void Animate() {
            if (this.Animations.Count == 0) return;

            foreach (var anim in this.Animations) {
                if (anim.Animating) {
                    this.Color = anim.ModifyColor(this.Color);
                    this.Position = anim.ModifyPosition(this.Position);
                    this.Rotation = anim.ModifyRotation(this.Rotation);
                    this.ScaleV = anim.ModifyScale(this.ScaleV);
                    this.Frame = anim.ModifyFrame(this.Frame);
                } else {
                    this.Animations.Remove(anim);
                    return;
                }
            }
        }
    }
}